#include <sys/types.h>
#if defined(LOONGARCH_2K1000)
#include "target/ls2k1000.h"
#elif defined(LOONGARCH_2K500)
#include "target/ls2k500.h"
#elif defined(LOONGARCH_2K300)
#include "target/ls2k300.h"
#endif
#include "target/bonito.h"
#include "../../../pmon/arch/loongarch/early_printf.c"
#include "../../../pmon/arch/loongarch/loongarch_delay.c"

#ifdef LOONGARCH_2K300
#include "target/mem_ctrl.h"
#include "./ddr4/ddr4_param_debug.c"
#define DDR_CFG_BASE  PHYS_TO_UNCACHED(0xff00000)
void loop_delay(uint64_t loops)
{
	volatile uint64_t counts = loops;
    counts = counts / 1000;
    if(counts <= 0)
        counts = 10;
    delay(counts);
}

ddr_ctrl mm_ctrl_info;
extern int ddr4_init (uint64_t node_num, ddr_ctrl *mm_ctrl);

void mm_feature_init(void)
{
#ifdef LOONGSON_3D5000
	mm_ctrl_info.mc_type = LS3D5000_MC_TYPE;
#elif LOONGSON_3C5000
	mm_ctrl_info.mc_type = LS3C5000_MC_TYPE;
#elif LOONGSON_2K300
	mm_ctrl_info.mc_type = LS2K0300_MC_TYPE;
#else
	mm_ctrl_info.mc_type = LS3A5000_MC_TYPE;
#endif
#ifdef LOONGSON_3D5000
	mm_ctrl_info.mc_interleave_offset = 12;
#elif LOONGSON_3A6000
	mm_ctrl_info.mc_interleave_offset = 13;
#else
	mm_ctrl_info.mc_interleave_offset = 8;
#endif

	mm_ctrl_info.mc_regs_base = DDR_CFG_BASE;
	mm_ctrl_info.cache_mem_base = 0xa000000000000000;
	mm_ctrl_info.dimm_info_in_flash_offset = DIMM_INFO_IN_FLASH_OFFS;
	mm_ctrl_info.ddr_freq = DDR_FREQ;
	mm_ctrl_info.ddr_freq_2slot = 400;//DDR_FREQ_2SLOT;
	mm_ctrl_info.node_offset = 44;//NODE_OFFSET;
	mm_ctrl_info.tot_node_num = TOT_NODE_NUM;
	mm_ctrl_info.node_mc_num = MC_PER_NODE;
	mm_ctrl_info.ref_clk = 100;

	mm_ctrl_info.spi_base = SPI_BASE;
	mm_ctrl_info.uart_base = UART_BASE;
	mm_ctrl_info.l2xbar_conf_addr = L2XBAR_CONF_ADDR;
	mm_ctrl_info.channel_width = 64;
	mm_ctrl_info.dll_bypass = 0;
	//if you want change kernel high start address you should change the macro
	mm_ctrl_info.mem_base = HIGH_MEM_WIN_BASE_ADDR;
	/* mm_ctrl is global variable */
#ifdef QUICK_START
	mm_ctrl_info.table.enable_early_printf		= 0;
#else
	mm_ctrl_info.table.enable_early_printf		= 1;
#endif
#ifdef DDR3_MODE
	mm_ctrl_info.table.ddr_param_store		= 1;
	mm_ctrl_info.table.ddr3_dimm			= 1;
	mm_ctrl_info.table.enable_mc_vref_training	= 0;
	mm_ctrl_info.table.enable_ddr_vref_training	= 0;
	mm_ctrl_info.table.enable_bit_training		= 0;
	mm_ctrl_info.table.disable_dimm_ecc		= 1;
#else
	mm_ctrl_info.table.ddr_param_store		= 1;
	mm_ctrl_info.table.ddr3_dimm			= 0;
	mm_ctrl_info.table.enable_mc_vref_training	= 1;
	mm_ctrl_info.table.enable_ddr_vref_training	= 1;
	mm_ctrl_info.table.enable_bit_training		= 0;
	mm_ctrl_info.table.disable_dimm_ecc		= 1;
#endif
	mm_ctrl_info.table.low_speed			= 0;
	mm_ctrl_info.table.auto_ddr_config		= 0;
	mm_ctrl_info.table.enable_ddr_leveling		= 1;
	mm_ctrl_info.table.print_ddr_leveling		= 1;
	mm_ctrl_info.table.vref_training_debug		= 0;
	mm_ctrl_info.table.bit_training_debug		= 1;
	mm_ctrl_info.table.enable_write_training	= 1;
	mm_ctrl_info.table.debug_write_training		= 0;
	mm_ctrl_info.table.print_dll_sample		= 0;
	mm_ctrl_info.table.disable_dq_odt_training	= 1;
	mm_ctrl_info.table.lvl_debug			= 1;
	mm_ctrl_info.table.disable_dram_crc		= 1;
	mm_ctrl_info.table.two_t_mode_enable		= 1;
	mm_ctrl_info.table.disable_read_dbi		= 1;
	mm_ctrl_info.table.disable_write_dbi		= 1;
	mm_ctrl_info.table.disable_dm			= 0;
	mm_ctrl_info.table.preamble2			= 0;
	mm_ctrl_info.table.set_by_protocol		= 1;
	mm_ctrl_info.table.param_set_from_spd_debug	= 0;
	mm_ctrl_info.table.refresh_1x			= 1;
	mm_ctrl_info.table.spd_only			= 0;
	mm_ctrl_info.table.ddr_debug_param		= 0;
	mm_ctrl_info.table.ddr_soft_clksel		= 1;
#ifdef LS_STR
	mm_ctrl_info.table.str				= 1;
#else
	mm_ctrl_info.table.str				= 0;
#endif
#ifdef DDR3_MODE
	mm_ctrl_info.vref.vref_range			= 0x01;
	mm_ctrl_info.vref.vref_value			= 0x78;
	mm_ctrl_info.table.pda_mode			= 0;
#else
	mm_ctrl_info.vref.vref_range			= 0x00;
	mm_ctrl_info.vref.vref_value			= 0x60;
	mm_ctrl_info.table.pda_mode			= 1;
#endif
	mm_ctrl_info.table.signal_test			= 0;
	mm_ctrl_info.vref.mc_vref_adjust		= 0x10;
	mm_ctrl_info.vref.ddr_vref_adjust		= 0x0;
	mm_ctrl_info.vref.vref_init			= 0x20;
	mm_ctrl_info.data.rl_manualy			= 0;
	mm_ctrl_info.data.bit_width			= 16;
	mm_ctrl_info.data.nc16_map			= 0;
	mm_ctrl_info.data.gate_mode			= 0;
	
	mm_ctrl_info.data.pad_reset_po                  = 0x0;

	mm_ctrl_info.data.wrlevel_count_low             = 0x0;
	mm_ctrl_info.vref.vref_bits_per                 = 0x0;
	mm_ctrl_info.vref.vref_bit 	                = 0x0;
	mm_ctrl_info.data.ref_manualy	                = 0x0;

	mm_ctrl_info.param.dll_ck_mc0			= 0x44;
	mm_ctrl_info.param.dll_ck_mc1			= 0x44;
#ifdef LOONGSON_3C5000
	mm_ctrl_info.param.dll_ck_mc2			= 0x44;
	mm_ctrl_info.param.dll_ck_mc3			= 0x44;
#endif
	mm_ctrl_info.param.RCD					= 0;
	mm_ctrl_info.param.RP					= 0;
	mm_ctrl_info.param.RAS					= 0;
	mm_ctrl_info.param.REF					= 0;
	mm_ctrl_info.param.RFC					= 0;

	mm_ctrl_info.ocd.pad_clk_ocd			= PAD_CLK_OCD;
	mm_ctrl_info.ocd.pad_ctrl_ocd			= PAD_CTRL_OCD;
	mm_ctrl_info.ocd.pad_ds_split			= PAD_DS_SPLIT_ALL;

	mm_ctrl_info.odt.rtt_nom_1r_1slot		= RTT_NOM;
	mm_ctrl_info.odt.rtt_park_1r_1slot		= RTT_PARK;
	mm_ctrl_info.odt.mc_dqs_odt_1cs			= MC_DQS_ODT;
	mm_ctrl_info.odt.mc_dq_odt_1cs			= MC_DQ_ODT;

	mm_ctrl_info.odt.rtt_nom_2r_1slot		= RTT_NOM_2RANK;
	mm_ctrl_info.odt.rtt_park_2r_1slot		= RTT_PARK_2RANK;

	mm_ctrl_info.odt.rtt_nom_1r_2slot_cs0		= RTT_NOM_CS0;
	mm_ctrl_info.odt.rtt_park_1r_2slot_cs0		= RTT_PARK_CS0;
	mm_ctrl_info.odt.rtt_nom_1r_2slot_cs1		= RTT_NOM_CS1;
	mm_ctrl_info.odt.rtt_park_1r_2slot_cs1		= RTT_PARK_CS1;

	mm_ctrl_info.odt.rtt_nom_2r_2slot_cs0		= RTT_NOM_2R_CS0;
	mm_ctrl_info.odt.rtt_park_2r_2slot_cs0		= RTT_PARK_2R_CS0;
	mm_ctrl_info.odt.rtt_nom_2r_2slot_cs2		= RTT_NOM_2R_CS2;
	mm_ctrl_info.odt.rtt_park_2r_2slot_cs2		= RTT_PARK_2R_CS2;

	mm_ctrl_info.odt.mc_dqs_odt_2cs			= MC_DQS_ODT_2CS;
	mm_ctrl_info.odt.mc_dq_odt_2cs			= MC_DQ_ODT_2CS;

	mm_ctrl_info.sameba_adj				= MC_PHY_REG_DATA_070;
	mm_ctrl_info.samebg_adj				= MC_PHY_REG_DATA_078;
	mm_ctrl_info.samec_adj				= MC_PHY_REG_DATA_080;
	mm_ctrl_info.samecs_adj				= MC_PHY_REG_DATA_090;
	mm_ctrl_info.diffcs_adj				= MC_PHY_REG_DATA_098;

	/* paster parameter */
	mm_ctrl_info.paster.mc0_enable			= MC0_ENABLE;
	mm_ctrl_info.paster.mc1_enable			= MC1_ENABLE;

	mm_ctrl_info.paster.mc0_memsize			= MC0_MEMSIZE;
	mm_ctrl_info.paster.mc0_dram_type		= MC0_DRAM_TYPE;
	mm_ctrl_info.paster.mc0_dimm_type		= MC0_DIMM_TYPE;
	mm_ctrl_info.paster.mc0_module_type		= MC0_MODULE_TYPE;
	mm_ctrl_info.paster.mc0_cid_num			= MC0_CID_NUM;
	mm_ctrl_info.paster.mc0_ba_num			= MC0_BA_NUM;
	mm_ctrl_info.paster.mc0_bg_num			= MC0_BG_NUM;
	mm_ctrl_info.paster.mc0_csmap			= MC0_CSMAP;
	mm_ctrl_info.paster.mc0_dram_width		= MC0_DRAM_WIDTH;
	mm_ctrl_info.paster.mc0_module_width		= MC0_MODULE_WIDTH;
	mm_ctrl_info.paster.mc0_ecc			= MC0_ECC;
	mm_ctrl_info.paster.mc0_sdram_capacity		= MC0_SDRAM_CAPACITY;
	mm_ctrl_info.paster.mc0_col_num			= MC0_COL_NUM;
	mm_ctrl_info.paster.mc0_row_num			= MC0_ROW_NUM;
	mm_ctrl_info.paster.mc0_addr_mirror		= MC0_ADDR_MIRROR;
	mm_ctrl_info.paster.mc0_bg_mirror		= MC0_BG_MIRROR;

	mm_ctrl_info.paster.mc1_memsize			= MC1_MEMSIZE;
	mm_ctrl_info.paster.mc1_dram_type		= MC1_DRAM_TYPE;
	mm_ctrl_info.paster.mc1_dimm_type		= MC1_DIMM_TYPE;
	mm_ctrl_info.paster.mc1_module_type		= MC1_MODULE_TYPE;
	mm_ctrl_info.paster.mc1_cid_num			= MC1_CID_NUM;
	mm_ctrl_info.paster.mc1_ba_num			= MC1_BA_NUM;
	mm_ctrl_info.paster.mc1_bg_num			= MC1_BG_NUM;
	mm_ctrl_info.paster.mc1_csmap			= MC1_CSMAP;
	mm_ctrl_info.paster.mc1_dram_width		= MC1_DRAM_WIDTH;
	mm_ctrl_info.paster.mc1_module_width		= MC1_MODULE_WIDTH;
	mm_ctrl_info.paster.mc1_ecc			= MC1_ECC;
	mm_ctrl_info.paster.mc1_sdram_capacity		= MC1_SDRAM_CAPACITY;
	mm_ctrl_info.paster.mc1_col_num			= MC1_COL_NUM;
	mm_ctrl_info.paster.mc1_row_num			= MC1_ROW_NUM;
	mm_ctrl_info.paster.mc1_addr_mirror		= MC1_ADDR_MIRROR;
	mm_ctrl_info.paster.mc1_bg_mirror		= MC1_BG_MIRROR;
	mm_ctrl_info.param_reg_array			= &param_info;
}

//extern void usb_phy_init(int sel);

/*
 * sel 0 usb, 1 otg
 */
static void ls2k0300_usb_phy_init(unsigned long long base, int sel, int delay_mult)
{
	unsigned int val;

	readl(base + 0x11c) &= ~(1 << (8 + sel));
	readl(base + 0x11c) |=  (1 <<  7);
	readl(base + 0x508) &= ~(1 << 3);

	if (sel) {
		readl(base + 0x508) |= (1 << 16) | (1 << 17);
	}

	readl(base + 0x508) |= (1 << 27);
	delay(100 * delay_mult);
	readl(base + 0x508) &= ~(1 << 27);
	readl(base + 0x508) |= (1 << (30 + sel));
	delay(200 * delay_mult);
	if (sel == 0)
		readl(base + 0x508) &= ~(1 << (28 + sel));

	delay(4000 * delay_mult);
	if (sel == 1)
		readl(base + 0x508) &= ~(1 << (28 + sel));
	readl(base + 0x508) |= (7 << 0);
	readl(base + 0x504) = (0x18) | (0x1<<25) | (0x1<<24) | (0x0<<26) | (0x1<<27);  
	readl(base + 0x504) = (0x18) | (0x1<<25) | (0x1<<24) | (0x0<<26) | (0x0<<27);  
	do {
	    val =  readl(base + 0x504);
	} while (!(val & (1 << 28)));

	readl(base + 0x500) |= 0x4;
	readl(base + 0x504) = (0x18) | (0x1<<25) | (0x1<<24) | (0x1<<26) | (0x1<<27);  //write 0x4 in phy-addr
	readl(base + 0x504) = (0x18) | (0x1<<25) | (0x1<<24) | (0x1<<26) | (0x0<<27);
	do {
	    val =  readl(base + 0x504);
	} while (!(val & (1 << 28)));

	delay(20 * delay_mult);

	readl(base + 0x508) |= (1 << 4);
	readl(base + 0x11c) |= (3 << 8);
	if (sel) {
		readl(base + 0x508) &= ~((1 << 16) | (1 << 17));
	}
}




#endif

void cache_main(unsigned long long msize)
{
#ifdef LOONGARCH_2K500
#ifdef LS_STR
	if (((readl(LS2K500_PM1_CNT_REG) >> 10) & 0x7) == SLEEP_TYPE_S3)
		realinit_loongarch();
	else
#endif
#endif
#ifdef LOONGARCH_2K300
	mm_feature_init();
	ddr4_init(TOT_NODE_NUM, &mm_ctrl_info);
	pr_info("mem init done!\n");

	*(volatile unsigned int *)(PHYS_TO_UNCACHED(0x16002108)) |= 0x80000000;
	//GMAC PAD & DC PAD
	*(volatile unsigned int *)(PHYS_TO_UNCACHED(0x16000110)) |= ((0x3 << 14) | (0x3 << 10));
	pr_info("usb init...\n");
	ls2k0300_usb_phy_init(PHYS_TO_UNCACHED(0x16000000), 0, 100); 
	ls2k0300_usb_phy_init(PHYS_TO_UNCACHED(0x16000000), 1, 100); 
	pr_info("usb init done!\n");
#if 0
	//SDIO
	/* config sdio and emmc mul_pin */
	*(volatile unsigned int *)(PHYS_TO_UNCACHED(0x160004a4)) |= 0xfff00000;
	*(volatile unsigned int *)(PHYS_TO_UNCACHED(0x160004a8)) |= 0xfffff;
	/* sdio0 -> emmc */
	*(volatile unsigned int *)(PHYS_TO_UNCACHED(0x161400fc)) |= 0x2;
	
	//DC
	/* config lcd mul_pin */
	*(volatile unsigned int *)(PHYS_TO_UNCACHED(0x16000490)) |= 0xffffffff;
	*(volatile unsigned int *)(PHYS_TO_UNCACHED(0x16000494)) |= 0xffffff;

	/* config i2c 0 1 2 3 */
	*(volatile unsigned int *)(PHYS_TO_UNCACHED(0x1600049c)) |= 0xffff;

	//GMAC0
	*(volatile unsigned int *)(PHYS_TO_UNCACHED(0x16000494)) |= (0xff << 24);
	*(volatile unsigned int *)(PHYS_TO_UNCACHED(0x16000498)) |= 0xffff;
	
	//GMAC1
	*(volatile unsigned int *)(PHYS_TO_UNCACHED(0x16000498)) |= (0x55 << 24);
	*(volatile unsigned int *)(PHYS_TO_UNCACHED(0x1600049c)) &= ~(0xffff);
	*(volatile unsigned int *)(PHYS_TO_UNCACHED(0x1600049c)) |= 0x5555;
#endif
	//GMAC PAD
	*(volatile unsigned int *)(PHYS_TO_UNCACHED(0x16000110)) |= (0x3 <<14);

#endif
	init_loongarch(msize);
}

#ifdef LOONGARCH_2K300
void dev_fixup(void)
{
	if (readl(SRAM_TOP) == 0x12345678) {
		readl(SRAM_TOP) = 0;
		return ;
	}
	ls2k0300_usb_phy_init(PHYS_TO_UNCACHED(0x16000000), 0, 0);
	ls2k0300_usb_phy_init(PHYS_TO_UNCACHED(0x16000000), 1, 0);
	readl(SRAM_TOP) = 0x12345678;
	readl(PHYS_TO_UNCACHED(0x16124000)) = 1;  // reboot
}
#endif
